#include "EpilNilObject.h"
#include "EpilInterpreter.h"
#include "EpilInterpreterException.h"
#include "EpilObjectHelper.h"


EpilNilObject::EpilNilObject( EpilInterpreter * interpreter )
: EpilValueObject( interpreter )
{
}


EpilNilObject::~EpilNilObject()
{
}


EpilValueObject * EpilNilObject::Clone() const
{
	return this->GetInterpreter()->CreateNilObject();
}


EpilObject * EpilNilObject::OperateUnary( EpilUnaryOperator op )
{
	switch( op )
	{
	case EUO_Opposite:
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a nil value.") );
	case EUO_Length:
		throw EpilInterpreterException( t("Attempt to get length of a nil value.") );
	case EUO_Not:
		return this->GetInterpreter()->CreateBooleanObject( true );
	}
	throw EpilInterpreterException( t("Invalid unary operator detected.") );
}


EpilObject * EpilNilObject::OperateBinary( EpilBinaryOperator op, EpilObject * that )
{
	switch( that->GetType() )
	{
	case EOT_Nil:
		{
			EpilNilObject * thatNil = static_cast<EpilNilObject*>( that );
			return this->OperateBinaryInternal( op, thatNil );
		}
	case EOT_Boolean:
		{
			EpilBooleanObject * thatBoolean = static_cast<EpilBooleanObject*>( that );
			return this->OperateBinaryInternal( op, thatBoolean );
		}
	case EOT_Number:
		{
			EpilNumberObject * thatDouble = static_cast<EpilNumberObject*>( that );
			return this->OperateBinaryInternal( op, thatDouble );
		}
	case EOT_String:
		{
			EpilStringObject * thatStr = static_cast<EpilStringObject*>( that );
			return this->OperateBinaryInternal( op, thatStr );
		}
	}
	return NULL;
}


EpilObject * EpilNilObject::OperateBinaryInternal( EpilBinaryOperator op, EpilNilObject * that )
{
	switch( op )
	{
	case EBO_And:
	case EBO_Or:
		return this->GetInterpreter()->CreateNilObject();
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( true );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( false );
	default:
		break;
	}

	if( EpilObjectHelper::IsComparisonOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to compare two nil values.") );
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a nil value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		throw EpilInterpreterException( t("Attempt to concatenate a nil value.") );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


EpilObject * EpilNilObject::OperateBinaryInternal( EpilBinaryOperator op, EpilBooleanObject * that )
{
	switch( op )
	{
	case EBO_And:
		return this->GetInterpreter()->CreateNilObject();
	case EBO_Or:
		return that->CreateCopy();
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( false );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( true );
	default:
		break;
	}

	if( EpilObjectHelper::IsComparisonOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to compare nil with boolean.") );
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a nil value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		throw EpilInterpreterException( t("Attempt to concatenate a nil value.") );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


EpilObject * EpilNilObject::OperateBinaryInternal( EpilBinaryOperator op, EpilNumberObject * that )
{
	switch( op )
	{
	case EBO_And:
		return this->GetInterpreter()->CreateNilObject();
	case EBO_Or:
		return that->CreateCopy();
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( false );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( true );
	default:
		break;
	}

	if( EpilObjectHelper::IsComparisonOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to compare nil with number.") );
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a nil value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		throw EpilInterpreterException( t("Attempt to concatenate a nil value.") );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


EpilObject * EpilNilObject::OperateBinaryInternal( EpilBinaryOperator op, EpilStringObject * that )
{
	switch( op )
	{
	case EBO_And:
		return this->GetInterpreter()->CreateNilObject();
	case EBO_Or:
		return that->CreateCopy();
	case EBO_Equal:
		return this->GetInterpreter()->CreateBooleanObject( false );
	case EBO_Unequal:
		return this->GetInterpreter()->CreateBooleanObject( true );
	default:
		break;
	}

	if( EpilObjectHelper::IsComparisonOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to compare nil with string.") );
	}

	if( EpilObjectHelper::IsArithmeticOperator( op ) )
	{
		throw EpilInterpreterException( t("Attempt to perform arithmetic on a nil value.") );
	}

	if( op == EBO_StringConcatenate )
	{
		throw EpilInterpreterException( t("Attempt to concatenate a nil value.") );
	}

	throw EpilInterpreterException( t("Invalid binary operator detected.") );
}


bool EpilNilObject::operator==( const EpilObject & rhs ) const
{
	if( rhs.GetType() != EOT_Nil )
	{
		return false;
	}
	const EpilNilObject & obj = static_cast<const EpilNilObject &>( rhs );
	return *this == obj;
}


bool EpilNilObject::operator==( const EpilNilObject & rhs ) const
{
	return true;
}


tstring EpilNilObject::ToString() const
{
	return t("nil");
}